/**
* AUTHOR: MARCUS BELCHER
* VER: 1.2
* DATE: 7/9/2011
* FILE: Map.h
* Storage class for the input data
* extends Grid template class
* Map stores "GridPoints" a stuct which stores necessary data
* This class uses the stored data to create a neighbour list
* using this list easy iteraction and searches can be done.
*/
#pragma once
#include "Grid.h"
#include "IslandGlobals.h"

class Map : public Grid<GridPoint> {
public:
	Map(int m_width, int m_height):Grid(m_width, m_height, GridPoint()),initialized(false){}
	~Map(){}
	
	bool Initialized(){
		neighbours = new vector<GridPoint* >[width*height];
		for(int i = 0; i < size;i++){
			neighbours[i].reserve(8);//max amount of neighbours
		}
		//set all grids with their index
		for(int i = 0; i < size;i++)arr[i].indexID = i;
		initialized = true;
		return initialized;
	}
	void PrintGrid(){
		vector<int> islandIDs;
		for(int i = 0; i < size;i++){
			if(!checkVectorForValue(islandIDs, arr[i].islandID))islandIDs.push_back(arr[i].islandID);
		}
		islandIDs.shrink_to_fit();

		cout << "- Printing Land Map -" << endl;
		for(int y = 0; y < HEIGHT;y++){//row iteration
			for(int x = 0; x < WIDTH;x++){
				if(arr[index(x,y)].isLand){
					int outputValue = getVectorPosition(islandIDs, arr[index(x,y)].islandID);
					cout << outputValue << ",";//getVectorPosition(islandIDs, arr[index(x,y)].islandID);
				}
				else cout << "_,";
			}
			cout << endl;
		}
	}

	string getMapDetails(vector<int> islandIDs){
		stringstream data;
		int *numOfTrees		= new int[islandIDs.size()];
		int *numOfBuildings	= new int[islandIDs.size()];
		int *numOfMountains	= new int[islandIDs.size()];
		for(unsigned int i = 0; i < islandIDs.size();i++){
			numOfMountains[i] = numOfBuildings[i] = numOfTrees[i] = 0;
		}
		for(int i = 0; i < size;i++){
			if(arr[i].feature == BUILDING){
				numOfBuildings[getVectorPosition(islandIDs, arr[i].islandID)]++;
			}
			if(arr[i].feature == TREE){
				numOfTrees[getVectorPosition(islandIDs, arr[i].islandID)]++;
			}
			if(arr[i].feature == MOUNTAIN){
				numOfMountains[getVectorPosition(islandIDs, arr[i].islandID)]++;
			}
		}

		data << "This map has a total of:" << endl;
		data << "# " << GetNumberOfIslands()<< " Islands"	<< endl;
		data << "# " << amountOfTrees		<< " Trees"		<< endl;
		data << "# " << amountOfBuildings	<< " Buildings" << endl;
		data << "# " << amountOfMountains	<< " Mountains" << endl;

		for(unsigned int i = 1; i < islandIDs.size();i++){
			data << "Island " << i	<< " has the following details: " << endl;
			data << "# Trees: "		<< numOfTrees[i] << endl;
			data << "# Buildings: "	<< numOfBuildings[i] << endl;
			data << "# Mountains: "	<< numOfMountains[i] << endl;
			
		}
		DELETE_ARRAY(numOfTrees);
		DELETE_ARRAY(numOfBuildings);
		DELETE_ARRAY(numOfMountains);
		return data.str();
	}
	string toString(){
		stringstream ss;
		ss.str("");
		vector<int> islandIDs;
		for(int i = 0; i < size;i++){
			if(!checkVectorForValue(islandIDs, arr[i].islandID))islandIDs.push_back(arr[i].islandID);
		}
		islandIDs.shrink_to_fit();
		ss << "- MapSolver by Marcus Belcher -" << endl;
		//ss << "This program took: " << programTime << " seconds to compute" << endl;
		ss << "- Map Details -" << endl;
		ss << getMapDetails(islandIDs);
		ss << "- Printing Land Map -" << endl;
		for(int y = 0; y < HEIGHT;y++){//row iteration
			for(int x = 0; x < WIDTH;x++){
				if(arr[index(x,y)].isLand){
					int outputValue = getVectorPosition(islandIDs, arr[index(x,y)].islandID);
					ss << outputValue << ",";//getVectorPosition(islandIDs, arr[index(x,y)].islandID);
				}
				else ss << "_,";
			}
			ss << endl;
		}
		return ss.str();
	}

	inline bool isLand(char c){
		if(c == TREE)		return true;
		if(c == LAND)		return true;
		if(c == MOUNTAIN)	return true;
		if(c == BUILDING)	return true;
		return false;
	}

	inline bool isBoundry(const int x, const int y){
		if(x < 0)return true;
		if(y < 0)return true;
		if(y >= HEIGHT)return true;
		if(x >= WIDTH)return true;
		return false;
	}

	const int GetNumberOfIslands(){
		vector<int> islandIDs;
		for(int i = 0; i < size;i++){
			if(!checkVectorForValue(islandIDs, arr[i].islandID)){
				if(arr[i].isLand){
					islandIDs.push_back(arr[i].islandID);
				}
			}
		}
		islandIDs.shrink_to_fit();
		return islandIDs.size();
	}

	void LinkNeighbours(){
		for(int x = 0; x < width; x++){
			for(int y = 0; y < height; y++){
				if(!isBoundry(x+1,y)	&&	arr[index(x+1,y)].isLand){		//left
					GridPoint* p =	&arr[index(x+1,y)];
					neighbours[index(x,y)].push_back(p);	
				}
				if(!isBoundry(x-1,y)	&& arr[index(x-1,y)].isLand){		//Right
					GridPoint* p = &arr[index(x-1,y)];
					neighbours[index(x,y)].push_back(p);
				}
				if(!isBoundry(x,y+1)	&& arr[index(x,y+1)].isLand){		//Up
					GridPoint* p = &arr[index(x,y+1)];
					neighbours[index(x,y)].push_back(p);
				}
				if(!isBoundry(x,y-1)	&& arr[index(x,y-1)].isLand){		//Down
					GridPoint* p = &arr[index(x,y-1)];
					neighbours[index(x,y)].push_back(p);
				}
				if(!isBoundry(x+1,y+1)	&& arr[index(x+1,y+1)].isLand){		//Top Right
					GridPoint* p = &arr[index(x+1,y+1)];
					neighbours[index(x,y)].push_back(p);
				}
				if(!isBoundry(x-1,y+1)	&& arr[index(x-1,y+1)].isLand){		//Top left
					GridPoint* p = &arr[index(x-1,y+1)];
					neighbours[index(x,y)].push_back(p);
				}
				if(!isBoundry(x-1,y-1)	&& arr[index(x-1,y-1)].isLand){		//Bottom Left
					GridPoint* p = &arr[index(x-1,y-1)];
					neighbours[index(x,y)].push_back(p);
				}
				if(!isBoundry(x+1,y-1)	&& arr[index(x+1,y-1)].isLand){		//Bottom Right
					GridPoint* p = &arr[index(x+1,y-1)];
					neighbours[index(x,y)].push_back(p);
				}
			}
		}

		ShrinkNeighbourVectors();
	}

	void ShrinkNeighbourVectors(){
		for(int i = 0; i < size;i++){
			neighbours[i].shrink_to_fit();
		}
	}

	const int NodeAmount(){return size;}
	vector<GridPoint* > getPointNeighbours(int x, int y)	{return neighbours[index(x,y)];}
	vector<GridPoint* > getPointNeighbours(int index)		{return neighbours[index];}
	void addPointNeighbours(int x, int y, GridPoint* point)	{return neighbours[index(x,y)].push_back(point);}

private:
	vector<GridPoint* > *neighbours;
	bool initialized;
};
